from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from langchain.llms import Ollama

from applications.common.utils.validate import str_escape
from applications.common.utils.http import table_api
from meilisearch import Client
from applications.extensions import db
from sqlalchemy import func
from sqlalchemy import and_
from applications.models import Book, Page, User, DictData, Favbook, Readpage, Booktags
from applications.schemas import BookOutSchema, PageOutSchema, ReadpageOutSchema
from flask_login import login_required, current_user, logout_user
from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.http import models
import markdown2

MEILISEARCH_URI = "http://192.168.2.230:7700"
MEILISEARCH_MASTERKEY = "janl-key"
meili_client = Client(MEILISEARCH_URI, MEILISEARCH_MASTERKEY)
collection_name = "chinese_medicine"
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
client = QdrantClient(host="192.168.2.230", port=6333)


bp = Blueprint('index', __name__, url_prefix='/')


# 首页
@bp.get('/')
def index():
    res = {}
    # key = str_escape(request.args.get('key', type=str))
    # if key:
    #     # print("key:", key)
    #     curr_index = meili_client.meilisearch("books")
    #     search_results = curr_index.search(key)
    #     print(search_results)
    #     # return table_api(data=search_results["hits"], count=search_results["estimatedTotalHits"])
    res["top_books"] = get_top_books()
    res["hot_books"] = get_hot_books()
    res["last_read"] = get_last_read_pages()
    res["new_books"] = get_newest_books()
    res["tag_books"] = get_tag_books()
    res["tags"] = get_tags()
    res["login_user"] = get_curr_user()
    return render_template('zbook/index.html', data=res)

def get_curr_user():
    usr = {}
    if current_user.is_authenticated:
        usr["id"] = current_user.id
        usr["username"] = current_user.username
        usr["realname"] = current_user.realname
        usr["avater"] = current_user.avatar
        usr["islogin"] = True
    else:
        usr["id"] = 0
        usr["username"] = "请登录"
        usr["realname"] = "请登录"
        usr["avater"] = "default_avater"
        usr["islogin"] = False
    return usr

def get_hot_books():
    books = Book.query.order_by(Book.views.desc()).limit(16).all()
    books_data = BookOutSchema(many=True).dump(books)
    return books_data


def get_top_books():
    top_books = []
    filters = []
    filters.append(DictData.enable==1)
    filters.append(DictData.type_code=="top_book")
    dict_topbook = DictData.query.filter(*filters).limit(4).all()
    for dt in dict_topbook:
        book = Book.query.filter(Book.id==int(dt.data_value)).first()
        book_data = BookOutSchema().dump(book)
        top_books.append(book_data)
    # print(top_books)
    return top_books

def get_last_read_pages():
    readpages = Readpage.query.outerjoin(Book, Readpage.bid == Book.id)\
        .outerjoin(Page, and_(Readpage.bid == Page.book_id, Readpage.pid == Page.sort))\
        .outerjoin(User, Readpage.uid == User.id)\
        .filter().with_entities(Readpage.id, Readpage.bid, Book.name, Book.tid, Book.tag,
                Readpage.pid, Page.sort, Page.title, User.realname, Readpage.create_time)\
        .order_by(Readpage.create_time.desc()).limit(11).all()
    rp_data = ReadpageOutSchema(many=True).dump(readpages)
    print(rp_data)
    return rp_data



def get_newest_books():
    books = Book.query.order_by(Book.create_time.desc()).limit(13).all()
    books_data = BookOutSchema(many=True).dump(books)
    print("newest books:", books_data)
    return books_data

def get_tag_books():
    data = []
    tags = DictData.query.filter(DictData.id >= 2, DictData.id < 8).all()
    for tag in tags:
        tb = {}
        tb["tid"] = tag.id
        tb["tag"] = tag.data_label
        tb["books"] = get_books_by_tid(tag.id)
        data.append(tb)
    print("tag_books:", data)
    return data

def get_books_by_tid(tid):
    data = {}
    books = Book.query.filter(Book.tid == tid).order_by(Book.views.desc()).limit(9).all()
    if len(books) > 0:
        first_book = BookOutSchema().dump(books[0])
        data["first_book"] = first_book
        data["other_books"] = []
        for bk in books:
            if bk.id == first_book["id"]:
                continue
            ob = {}
            ob["id"] = bk.id
            ob["name"] = bk.name
            ob["auth"] = bk.auth
            data["other_books"].append(ob)
    return data


def get_tags():
    data = []
    tcs = db.session.query(Book.tid, func.count(Book.id)).group_by(Book.tid).all()
    for tid, cnt in tcs:
        print("tid:", tid, "cnt:", cnt)
        if not tid:
            tid = 0
        t = {}
        t["tid"] = tid
        t["tag"] = get_booktag_name(tid)
        t["count"] = cnt
        data.append(t)
    print("tags:", data)
    return data

def get_booktag_name(tid):
    if tid == 0:
        return "未分类"
    tag = DictData.query.filter(DictData.id == tid).first()
    if tag:
        return tag.data_label
    else:
        return "未分类"


@bp.get('/search')
def search():
    search_results = {}
    key = str_escape(request.args.get('key', type=str))
    page = str_escape(request.args.get('page', type=str))
    limit = str_escape(request.args.get('limit', type=str))
    if not page:
        page = 1
    else:
        page = int(page)
    if not limit:
        limit = 10
    else:
        limit = int(limit)
    if key:
        query_vector = model.encode(key)
        print("query_vector:", query_vector)
        # curr_index = meili_client.index("books")
        # curr_index.update_searchable_attributes(['content'])
        # search_params = {
        #    "q": key,  # 搜索关键字
        #    "attributesToHighlight": ["title", "content"],  # 需要高亮的字段
        #    "highlightPreTag": '<span class="keyword">',  # 自定义高亮前缀标签
        #    "highlightPostTag": '</span>',  # 自定义高亮后缀标签
        #    "attributesToCrop": ["content"],  # 需要裁剪的字段
        #    "cropLength": 250,  # 裁剪长度
        #    "limit": limit,
        #    "offset": page
        #}
        # search_results = curr_index.search("", search_params)
        # search_results = curr_index.search(key, {'limit': limit, 'offset': page, 'highlightPreTag': '<em>', 'highlightPostTag': '</em>'})
        # 执行查询
        # params = models.QueryParams(
        #     limit=5,  # 最多返回 5 条结果
        #     distance=models.Distance.COSINE  # 使用余弦相似度作为距离度量
        # )

        # 执行查询
        hits = client.search(
            collection_name=collection_name,
            query_vector=query_vector,
            limit=limit,
            offset=page
        )
        print("print:", hits)
        # for p in search_results["hits"]:
        #     book = Book.query.filter(Book.id == p["book_id"]).first()
        #     book_data = BookOutSchema().dump(book)
        #     p["book"] = book_data
        search_results["hits"] = []
        for p in hits:
            point = p.payload
            point["score"] = p.score
            book = Book.query.filter(Book.id == p.payload["bid"]).first()
            book_data = BookOutSchema().dump(book)
            point["book"] = book_data
            search_results["hits"].append(point)

        search_results["top_books"] = get_top_books()
        search_results["page_navs"] = get_search_page_navs(key, page, limit, len(hits))
        search_results["login_user"] = get_curr_user()
        print(search_results)
        # return table_api(data=search_results["hits"], count=search_results["estimatedTotalHits"])
    return render_template('zbook/search.html', data=search_results)


def get_search_page_navs(key, page, limit, total):
    page_navs = []
    if not key:
        key = ''
    pn = {"name": "first", "href": "/search?key=" + key + "&page=" + str(page) + "&limit=" + str(limit), "current": False}
    page_navs.append(pn)
    if page < 3:
        for i in range(1, 6):
            if i == page:
                pn = {"name": str(i), "href": "#", "current": True}
            else:
                pn = {"name": str(i), "href": "/search?key=" + key + "&page=" + str(i) + "&limit=" + str(limit), "current": False}
            page_navs.append(pn)
    else:
        for i in range(page - 2, page + 3):
            if i == page:
                pn = {"name": str(i), "href": "#", "current": True}
            else:
                pn = {"name": str(i), "href": "/search?key=" + key + "&page=" + str(i) + "&limit=" + str(limit), "current": False}
            page_navs.append(pn)

    last = int(total / limit)
    if total % limit > 0:
        last = last + 1
    pn = {"name": "last", "href": "/search?key=" + key + "&page=" + str(last) + "&limit=" + str(limit), "current": False}
    page_navs.append(pn)
    return page_navs

@bp.get('/bookself')
@login_required
def bookself():
    bookid = str_escape(request.args.get('bid', type=str))
    op = str_escape(request.args.get('op', type=str))

    if op and bookid:
        bid = int(bookid)
        if op=="unfav":
            book = Book.query.filter(Book.id==bid).first()
            fb = Favbook.query.filter(Favbook.bid == bid, Favbook.uid == current_user.id).delete()
            book.favs = book.favs - 1
            if book.favs < 0:
                book.favs = 0
            db.session.add(book)
            db.session.commit()

    res = {}
    books = Book.query.join(Favbook, Book.id==Favbook.bid).filter(Favbook.uid==current_user.id).all()
    books_data = BookOutSchema(many=True).dump(books)
    res["books"] = books_data
    res["top_books"] = get_top_books()
    res["login_user"] = get_curr_user()
    print(res)
    return render_template('zbook/bookself.html', data=res)

@bp.get('/books')
def books():
    book_name = str_escape(request.args.get('key', type=str))
    filters = []
    books_data = []
    print("book_name:", book_name)
    if book_name:
        filters.append(Book.name.contains(book_name))
    books = Book.query.filter(*filters).all()
    books_data = BookOutSchema(many=True).dump(books)
    print(books_data)
    return render_template('zbook/books.html', data=books_data)

@bp.get('/list')
def list():
    key = str_escape(request.args.get('key', type=str))
    tid = str_escape(request.args.get('tid', type=str))
    page = str_escape(request.args.get('page', type=str))
    limit = str_escape(request.args.get('limit', type=str))
    filters = []
    if key:
        filters.append(Book.name.contains(key))
    if tid:
        tid = int(tid)
        if tid > 0:
            filters.append(Book.tid == tid)
        elif tid == 0:
            filters.append(Book.tid == None)
    if not page:
        page = 1
    else:
        page = int(page)
    if not limit:
        limit = 12
    else:
        limit = int(limit)
    books = Book.query.filter(*filters).paginate(page=page, per_page=limit, error_out=False)

    res = {}
    res["msg"] = "OK"
    res["code"] = 0
    res["data"] = BookOutSchema(many=True).dump(books)
    res["count"] = books.total
    res["limit"] = limit
    res["page"] = page
    res["key"] = key
    res["page_navs"] = get_list_page_navs(key, tid, page, limit, books.total)
    res["top_books"] = get_top_books()
    res["login_user"] = get_curr_user()
    print(res)
    return render_template('zbook/list.html', data=res)

def get_list_page_navs(key, tid, page, limit, total):
    page_navs = []
    if not key:
        key = ''
    if not tid:
        tid = ''
    pn = {"name": "first", "href": "/list?tid=" + str(tid) + "&key=" + key + "&page=" + str(page) + "&limit=" + str(limit), "current": False}
    page_navs.append(pn)
    max = int(total / limit)
    if total % limit > 0:
        max = max + 1
    print("page:", page, "limit:", limit, "total:", total, "max:", max)
    if page < 3:
        if max > 6:
            max = 6
        for i in range(1, max+1):
            if i == page:
                pn = {"name": str(i), "href": "#", "current": True}
            else:
                pn = {"name": str(i), "href": "/list?tid=" + str(tid) + "&key=" + key + "&page=" + str(i) + "&limit=" + str(limit), "current": False}
            page_navs.append(pn)
    else:
        if max > (page + 3):
            max = page + 3
        for i in range(page - 2, max+1):
            if i == page:
                pn = {"name": str(i), "href": "#", "current": True}
            else:
                pn = {"name": str(i), "href": "/list?tid=" + str(tid) + "&key=" + key + "&page=" + str(i) + "&limit=" + str(limit), "current": False}
            page_navs.append(pn)

    last = int(total / limit)
    if total % limit > 0:
        last = last + 1
    pn = {"name": "last", "href": "/list?tid=" + str(tid) + "&key=" + key + "&page=" + str(last) + "&limit=" + str(limit), "current": False}
    page_navs.append(pn)
    return page_navs

@bp.get('/catalog')
@login_required
def catalog():
    bookid = str_escape(request.args.get('bid', type=str))
    op = str_escape(request.args.get('op', type=str))
    tags = str_escape(request.args.get('tags', type=str))
    rmtag = str_escape(request.args.get('rmtag', type=str))
    if not bookid:
        bookid = "1"
    bid = int(bookid)
    book = Book.query.filter(Book.id == bid).first()
    if op:
        if op == "fav":
            fb = Favbook(bid=bid, uid=current_user.id, status=1, sort=get_max_fav_book_sort(bid,current_user.id))
            db.session.add(fb)
            if not book.favs:
                book.favs = 0
            book.favs = book.favs + 1
            db.session.add(book)
            db.session.commit()
        elif op == "unfav":
            fb = Favbook.query.filter(Favbook.bid==bid, Favbook.uid==current_user.id).delete()
            book.favs = book.favs - 1
            if book.favs < 0:
                book.favs = 0
            db.session.add(book)
            db.session.commit()
    if tags and bookid:
        bid = int(bookid)
        tagidlst = tags.split(',')
        for tid_str in tagidlst:
            tid = int(tid_str)
            print("bid:", bid, "tid:", tid, "uid:", current_user.id)
            bt = Booktags(uid=current_user.id, bid=bid, tid=tid, count=1)
            db.session.add(bt)
        db.session.commit()
        # update book tag
        book_tags = get_most_tags(bid)
        if len(book_tags) > 0 and book:
            if book.tag != book_tags[0]["tag"]:
                book.tag = book_tags[0]["tag"]
                book.tid = book_tags[0]["tid"]
                db.session.add(book)
                db.session.commit()
    if rmtag and bookid:
        bid = int(bookid)
        tid = int(rmtag)
        Booktags.query.filter(Booktags.uid==current_user.id,Booktags.bid==bid,Booktags.tid==tid).delete()
        db.session.commit()
        # update book tag.
        book_tags = get_most_tags(bid)
        if len(book_tags) > 0 and book:
            if book.tag != book_tags[0]["tag"]:
                book.tag = book_tags[0]["tag"]
                book.tid = book_tags[0]["tid"]
                db.session.add(book)
                db.session.commit()

    pages = Page.query.filter(Page.book_id==book.id).with_entities(Page.id, Page.book_id, Page.title, Page.status, Page.sort).order_by(Page.sort).all()
    book_data = BookOutSchema().dump(book)
    pages_data = PageOutSchema(many=True).dump(pages)
    book_tags = get_most_tags(bid)
    data = {}
    data["book"] = book_data
    data["pages"] = pages_data
    data["tags"] = book_tags
    c = Favbook.query.filter(Favbook.bid == bid, Favbook.uid == current_user.id).count()
    if c > 0:
        data["isfav"] = True
    else:
        data["isfav"] = False
    print("data:", data)
    data["login_user"] = get_curr_user()
    return render_template('zbook/catalog.html', data=data)

def get_tag_name(tid):
    tag_name = ""
    tag = DictData.query.filter(DictData.id==tid).first()
    tag_name = tag.data_label
    return tag_name

def get_most_tags(bid):
    tags = []
    result = db.session.query(Booktags.tid, func.sum(Booktags.count)).filter(Booktags.bid==bid)\
        .group_by(Booktags.tid).order_by(func.sum(Booktags.count).desc()).all()
    for tid, cnt in result:
        # print("tid:", tid, "count:", cnt)
        tag_name = get_tag_name(tid)
        tags.append({"tid": tid, "count": cnt, "tag": tag_name})
    return tags

def get_max_fav_book_sort(bid, uid):
    s = db.session.query(func.max(Favbook.sort)).filter(Favbook.bid==bid,Favbook.uid==current_user.id).scalar()
    return s

@bp.get('/read')
@login_required
def read():
    key = str_escape(request.args.get('key', type=str))
    if not key:
        key = ""
    bookid = str_escape(request.args.get('bid', type=str))
    if not bookid:
        bookid = "1"
    pageid = str_escape(request.args.get('pid', type=str))
    if not pageid:
        pageid = "1"
    bid = int(bookid)
    pid = int(pageid)
    print("booid:", bid, "pageid:", pid)
    data = {}
    page = Page.query.filter(Page.book_id==bid, Page.sort==pid).first()
    book = Book.query.filter(Book.id==bid).first()
    if not book.views:
        book.views = 0
    book.views = book.views + 1
    db.session.add(book)
    rp = Readpage(uid=current_user.id, bid=bid, pid=pid)
    db.session.add(rp)
    db.session.commit()
    page_data = PageOutSchema().dump(page)
    if key:
        page_data["content"] = page_data["content"].replace(key, '<span class="keyword">'+key+'</span>')
    print(page)
    print(page.sort)
    pre_page = page.sort - 1
    next_page = page.sort + 1
    pre_title = Page.query.filter(Page.book_id==bid,Page.sort==pre_page).with_entities(Page.id,Page.book_id,Page.title,Page.status,Page.sort).first()
    pre_title_data = PageOutSchema().dump(pre_title)
    next_title = Page.query.filter(Page.book_id==bid,Page.sort==next_page).with_entities(Page.id,Page.book_id,Page.title,Page.status,Page.sort).first()
    next_title_data = PageOutSchema().dump(next_title)
    data["page"] = page_data
    data["pre_title"] = pre_title_data
    data["next_title"] = next_title_data
    data["top_books"] = get_top_books()
    data["login_user"] = get_curr_user()
    print("data:", data)
    return render_template('zbook/read.html', data=data)


@bp.get('/page')
def page():
    bookid = str_escape(request.args.get('bid', type=str))
    if not bookid:
        bookid = "1"
    pageid = str_escape(request.args.get('pid', type=str))
    if not pageid:
        pageid = "1"
    bid = int(bookid)
    pid = int(pageid)
    print("booid:", bid, "pageid:", pid)
    page = Page.query.filter(Page.book_id == bid, Page.sort == pid).first()
    page_data = PageOutSchema().dump(page)
    print("book_data:", page_data)
    return table_api(data=page_data, count=1)


@bp.get('/reader')
def reader():
    bookid = str_escape(request.args.get('bid', type=str))
    if not bookid:
        bookid = "1"
    pageid = str_escape(request.args.get('pid', type=str))
    if not pageid:
        pageid = "1"
    bid = int(bookid)
    pid = int(pageid)
    print("booid:", bid, "pageid:", pid)
    book = Book.query.filter(Book.id == bid).first()
    book_data = BookOutSchema().dump(book)
    print("book_data:", book_data)
    pages = Page.query.filter(Page.book_id == bid).order_by(Page.sort).all()
    page_data = PageOutSchema(many=True).dump(pages)
    print("page_data:", page_data)
    return render_template('zbook/reader.html', book=book_data, pages=page_data)

@bp.get('/logout')
def logout():
    logout_user()
    return redirect(url_for('index.index'))
    # res = {}
    # res["top_books"] = get_top_books()
    # res["hot_books"] = get_hot_books()
    # res["last_read"] = get_last_read_pages()
    # res["new_books"] = get_newest_books()
    # res["tag_books"] = get_tag_books()
    # res["tags"] = get_tags()
    # res["login_user"] = get_curr_user()
    # return render_template('zbook/index.html', data=res)


@bp.get('/chat')
def chat():
    res = {}
    res["login_user"] = get_curr_user()
    return render_template("zbook/chat.html", data=res)


def get_knowledge_question(question):
    query_vector = model.encode(question)
    # print("query_vector:", query_vector)
    hits = client.search(
        collection_name=collection_name,
        query_vector=query_vector,
        limit=5
    )
    # print("print:", hits)
    knowledge = ""
    for k in hits:
        knowledge = knowledge + k.payload["content"]
    print("knowledge:", knowledge)
    return knowledge

@bp.post('/ask')
def ask():
    question = request.form["user_input"]
    model = Ollama(model="deepseek-r1:14b", base_url="http://127.0.0.1:11434")
    # model = Ollama(model="deepseek-r1:14b")
    if not question:
        return jsonify({"error": "Question is required"}), 400

    prompt = """
    ##角色定位：\n
    您是一位已经有50余年行医经历的经方老中医，擅长用经方，针炙，推拿等治疗方法解决各种疑难杂症。\n
    ##服务目标：\n
    根据用户描述的证状结合中医的辩证思维参考积累的医案，提供解决方案和处方建议；\n
    解释症状的形成原因，辩证过程，分析患者证状形成原因，然后确立治疗方案。\n
    ##必备技能：\n
    深厚的中医辩证功底，熟悉多种治疗手段，如方药，针炙，推拿，具有丰富的治疗经验；\n
    擅于通过循循善诱的沟通和问询，梳理用户的证状，然后根据经验进行精准辩证。\n
    凭借优秀的辩证能力进行证状分析能力，能够深入浅出的讲明病机，病因，并制定治疗方案。\n
    ##创作流程：\n
    通过与用户的询问与沟通，梳理症状描述和证状描述；\n
    然后由浅入深的分析证状形成原因和病证机理；\n
    参考历代名家经方医案，提供治疗方案和优化治疗方案；\n
    解释辨证原理和对应治疗方案细节或用药思路。\n
    ##注意事项：\n
    请确保提供清晰的证状描术和求医述求；\n
    解决方案需尽量参考资料中的经方或医案中经过验证的方剂。\n
    请结合以下资料，回答提问。\n
    ##参考资料:\n
    """
    knowledge = get_knowledge_question(question)
    prompt = prompt + knowledge

    question = "\n##用户问题：\n" + question
    prompt = prompt + question

    response = model(
        prompt=prompt,
        max_tokens=4096,
        temperature=0.7,
        top_p=0.9,
        context_length=32
    )
    # response = model.complete(f"基于以下知识：中医知识回答以下问题。\n\n问题：{question}")
    # print("response:", response)
    pos = response.find("</think>") + 8
    think = response[:pos]
    answer = response[pos:]
    print("think:", think)
    print("answer:", answer)
    html_content = think + markdown2.markdown(answer)
    return jsonify({
        "answer": html_content,
        "status": "success"
    }), 200